#ifndef __SDIO_H_
#define __SDIO_H_

/* SDIO AHB related Registers */

#define SDIO_AHB_BASE			0xB0300000

#define SDIO_CHNL_STATUS        (SDIO_AHB_BASE + 0x00)	/* byte */
#define SDIO_CHNL0_TRNSFR_LEN   (SDIO_AHB_BASE + 0x04)	/* word */
#define SDIO_CHNL1_TRNSFR_LEN   (SDIO_AHB_BASE + 0x06)	/* word */
#define SDIO_DMA_INT_STATUS     (SDIO_AHB_BASE + 0x08)	/* byte */
#define SDIO_DMA_INT_PND        (SDIO_AHB_BASE + 0x0C)	/* byte */
#define SDIO_DMA_INT_MSK        (SDIO_AHB_BASE + 0x10)	/* byte */
#define SDIO_DMA_TIMEOUT        (SDIO_AHB_BASE + 0x11)	/* byte */
#define SDIO_CNTRL_REG          (SDIO_AHB_BASE + 0x14)	/* word */
#define SDIO_AHB_WRITE_ADDR     (SDIO_AHB_BASE + 0x18)	/* long */
#define SDIO_AHB_READ_ADDR      (SDIO_AHB_BASE + 0x1C)	/* long */
#define SDIO_AHB2SDIO_LEN       (SDIO_AHB_BASE + 0x20)	/* word */
#define SDIO_SDIO2AHB_LEN       (SDIO_AHB_BASE + 0x22)	/* word */
#define SDIO_INT_STATUS         (SDIO_AHB_BASE + 0x24)	/* word */
#define SDIO_INT_PND            (SDIO_AHB_BASE + 0x28)	/* word */
#define SDIO_INT_MSK            (SDIO_AHB_BASE + 0x2C)	/* word */
#define SDIO_TIMER_TERM_CNT     (SDIO_AHB_BASE + 0x2E)	/* word */
#define SDIO_HW_VERSION_REG     (SDIO_AHB_BASE + 0x30)	/* word */
#define SDIO_REG_RDWR_DATA      (SDIO_AHB_BASE + 0x34)	/* long */
#define SDIO_STATE_CONTROL_REG  (SDIO_AHB_BASE + 0x38)	/* byte */
#define SDIO_FIFO_TF_STATUS_REG (SDIO_AHB_BASE + 0x39)	/* byte */
#define SDIO_MAP_REG			 SDIO_REG_RDWR_DATA

/* Control Register Bits */
#define SDIO_AHB_RD_ADDR_VALID     0x1
#define SDIO_AHB_WR_ADDR_VALID     0x2
#define SDIO_INT_AFTER_FIFO_FULL   0x4
#define SDIO_AHB_BUS_NO_INCR       0x40
#define RD_FIFO_PNTR_RST_EN        0x80
#define WR_FIFO_PNTR_RST_EN        0x100

/* Interrupt Status Register Bits */
#define SDIO_2_AHB_TX_COMP         0x1
#define SDIO_2_AHB_INVLD_LEN       0x2
#define SDIO_2_AHB_INVLD_BRST_LEN  0x4
#define AHB_2_SDIO_TX_COMP         0x8
#define AHB_2_SDIO_INVLD_LEN       0x10
#define AHB_2_SDIO_INVLD_BRST_LEN  0x20
#define DMA_INT                    0x40
#define AHB_2_SDIO_TX_ABORT        0x80
#define SDIO_2_AHB_TX_ABORT        0x100
#define HOST_RESET				   0x200
#define HOST_2_C_INT			   0x400

/* SDIO2ABH Length Register Bits */
#define SDIO2AHB_LEN_VALID         0x8000

/* DMA Interrupt Status Register Bits */
#define SDIO_2_AHB_DMA_COMP        0x1
#define SDIO_2_AHB_BUS_ERR         0x2
#define SDIO_2_AHB_SIG_ERR         0x4
#define SDIO_2_AHB_DMA_TIMEOUT     0x8
#define AHB_2_SDIO_DMA_COMP        0x10
#define AHB_2_SDIO_BUS_ERR         0x20
#define AHB_2_SDIO_SIG_ERR         0x40
#define AHB_2_SDIO_DMA_TIMEOUT     0x80

/* SD Access Bits */
#define SD_VALID_BIT               BIT(29)
#define SD_RD_WR_BIT               BIT(28)

/* SD State Control Register Bits */
#define START_CARD_INIT_EN         BIT(0)
#define CARD_READY_FLAG            BIT(1)

#define SDIO_CHNL_LEN_VAL          64    /* Burst size                   */
#define SDIO_MAX_BUFSZ             1550  /* Maximum size of a dma buffer */
//#define SDIO_MAX_BUFSZ             1400  /* Maximum size of a dma buffer */
#define FUNC_BLOCK_SIZE            128   /* Function Blk Size in Blk mode*/

/* SDIO CCCR related Registers */
#define rSDIO_CCCR_REV            0x00
#define rSDIO_SD_SPECS_REV        0x01
#define rSDIO_IO_ENABLE           0x02
#define rSDIO_IO_READY            0x03
#define rSDIO_INT_ENABLE          0x04
#define rSDIO_INT_PENDING         0x05
#define rSDIO_IO_ABORT            0x06
#define rSDIO_BUS_IF_CTRL         0x07
#define rSDIO_CARD_CAP            0x08
#define rSDIO_COMMON_CIS          0x09
#define rSDIO_BUS_SUSPEND         0x0C
#define rSDIO_FN_SELECT           0x0D
#define rSDIO_EXEC_FLAGS          0x0E
#define rSDIO_RDY_FLAGS           0x0F
#define rSDIO_FN0_BLK_SIZE        0x10
#define rSDIO_POWER_CTRL          0x12

/* SDIO FBR related Registers */
#define rSDIO_FN1_STD_FN_IF_CODE     0x100
#define rSDIO_FN1_ETN_STD_FN_IF_CODE 0x101
#define rSDIO_FN1_POWER_CTRL         0x102
#define rSDIO_FN1_CIS                0x109
#define rSDIO_FN1_CSA                0x10C
#define rSDIO_FN1_CS_WINDOW          0x10F
#define rSDIO_FN1_BLK_SIZE           0x110

/* Common CIS Parameters */
#define SDIO_COMMON_CIS_PTR          0x001000
#define SDIO_FN1_CIS_PTR             0x001100

/* MAC ADDRESS CIS pointer */
#define SDIO_MAC_ADDR_CIS_PTR        0x001020

#define HOST_RX_EXTRA_OFFSET		0x0

typedef enum {
	SDIO_STATE_UNSPEC = 0x0,
	SDIO_STATE_IDLE = 0x1,
	SDIO_STATE_CMD5_RECVD = 0x2,
	SDIO_STATE_WAIT_FOR_IE = 0x3,
	SDIO_STATE_IO_ENABLED = 0x4,
} sdio_state_t;

/*****************************************************************************/
/* Extern variable declarations                                              */
/*****************************************************************************/
extern u32  g_sdio_state;

/*****************************************************************************/
/* Extern Function Declarations                                              */
/*****************************************************************************/
extern void sdio_init(void);

/*****************************************************************************/
/* Inline Functions                                                          */
/*****************************************************************************/

/* This function puts the buffer address into the Rx hardware ring queue */
static inline __inline void set_sdio_chnl_len(u16 len)
{
	setw(SDIO_CHNL0_TRNSFR_LEN, len);
	setw(SDIO_CHNL1_TRNSFR_LEN, len);
}

/* This function sets the control Register */
static inline __inline void set_sdio_ctrl_reg(u16 val)
{
    setw(SDIO_CNTRL_REG, val);
}

/* This function sets the DMA Timeout Value */
static inline __inline void set_sdio_dma_timeout(u8 val)
{
    setb(SDIO_DMA_TIMEOUT, val);
}

/* This function sets the DMA Interrupt MASK */
static inline __inline void set_sdio_dma_int_msk(u8 msk)
{
    setb(SDIO_DMA_INT_MSK, msk);
}

/* This function sets the Interrupt MASK */
static inline __inline void set_sdio_int_msk(u16 msk)
{
    setw(SDIO_INT_MSK, msk);
}

/* This function clears the DMA Interrupt pending bit */
static inline __inline void clr_sdio_dma_int_pnd(u8 bit)
{
    setb(SDIO_DMA_INT_PND, bit);
}

/* This function clears the Interrupt pending bit */
static inline __inline void clr_sdio_int_pnd(u16 bit)
{
    setw(SDIO_INT_PND, bit);
}

/* This function returns SDIO interrupt status */
static inline __inline u16 get_sdio_int_status(void)
{
    return getw(SDIO_INT_STATUS);
}

/* This function returns SDIO interrupt status */
static inline __inline u8 get_sdio_dma_int_status(void)
{
    return getb(SDIO_DMA_INT_STATUS);
}

/* This function puts the buffer address into the Rx hardware ring queue */
static inline __inline void put_rx_sdio_hw_q(u32 buf_add)
{
	u32 temp = 0;

	set(SDIO_AHB_WRITE_ADDR, buf_add);
    temp                = get(SDIO_CNTRL_REG);
    set(SDIO_CNTRL_REG, (temp & ~SDIO_AHB_RD_ADDR_VALID) | SDIO_AHB_WR_ADDR_VALID);
}

/* This function puts the buffer address into the Rx hardware ring queue */
static inline __inline u32 get_rx_sdio_hw_q(void)
{
    return get(SDIO_AHB_WRITE_ADDR);
}

/* This function puts the buffer address into the Rx hardware ring queue */
static inline __inline u16 get_rx_length_sdio(void)
{
    return getw(SDIO_SDIO2AHB_LEN) & 0x7FFF;
}

/* This function puts the buffer address into the Tx hardware ring queue */
static inline __inline void put_tx_sdio_hw_q(u32 buf_add, u16 length)
{
    u32 temp = 0;
    set(SDIO_AHB_READ_ADDR, buf_add);
    setw(SDIO_AHB2SDIO_LEN, length);

    /* Set Read Valid to 1 with out modifing other bits   */
    /* Avoid touching Write Valid by writing a zero to it */
    temp                 = get(SDIO_CNTRL_REG);
	set(SDIO_CNTRL_REG, (temp & ~SDIO_AHB_WR_ADDR_VALID) | SDIO_AHB_RD_ADDR_VALID);
}

/* This function checks for full state of Tx queue. This function returns 1, */
/* if it is full. 0, if it is not full                                       */
static inline __inline u32 is_tx_sdio_hw_q_full(void)
{
    if(get(SDIO_CNTRL_REG) & SDIO_AHB_RD_ADDR_VALID)
        return 1;

    return 0;
}

static inline __inline void put_tx_sdio_hw_q_sync(u32 buf_add, u16 length)
{
    u32 temp = 0;

    while(get(SDIO_CNTRL_REG) & SDIO_AHB_RD_ADDR_VALID);
	set(SDIO_AHB_READ_ADDR, 0x00FFFFFF & buf_add);
    setw(SDIO_AHB2SDIO_LEN, length);

    /* Set Read Valid to 1 with out modifing other bits   */
    /* Avoid touching Write Valid by writing a zero to it */
    temp                 = get(SDIO_CNTRL_REG);
    set(SDIO_CNTRL_REG, (temp & ~SDIO_AHB_WR_ADDR_VALID) | SDIO_AHB_RD_ADDR_VALID);
}

/* Clear the interrupt */
static inline __inline void sdio_clear_int(u16 interrupt)
{
	setw(SDIO_INT_PND, interrupt);
}

/* This function masks given int */
static inline __inline void sdio_mask_int(u16 mask)
{
	orw(SDIO_INT_MSK, mask);
}

/* This function unmasks given int */
static inline __inline void sdio_unmask_int(u16 mask)
{
	andw(SDIO_INT_MSK, ~mask);
}

/* Clear the interrupt */
static inline __inline void sdio_clear_dma_int(u8 interrupt)
{
	setb(SDIO_DMA_INT_PND, interrupt);
}

/* This function masks given int */
static inline __inline void sdio_mask_dma_int(u8 mask)
{
    orb(SDIO_DMA_INT_MSK, mask);
}

/* This function unmasks given int */
static inline __inline void sdio_unmask_dma_int(u8 mask)
{
	andb(SDIO_DMA_INT_MSK, ~mask);
}

/* This function sets the timer termination counter value */
static inline __inline void sdio_timer_term_cnt(u16 val)
{
	andw(SDIO_TIMER_TERM_CNT, val);
}

/* This function returns SDIO Register value */
static inline __inline u8 sdio_read_sd_reg(u32 addr)
{
    u32 temp = 0;
    while(get(SDIO_REG_RDWR_DATA) & SD_VALID_BIT)
        temp += 1;

    temp = ((((addr & 0x00FFFFFF) << 8) & 0x0FFFFF00)) | SD_VALID_BIT;

    set(SDIO_REG_RDWR_DATA, temp);

    while(get(SDIO_REG_RDWR_DATA) & SD_VALID_BIT)
        temp += 1;

    return (u8)(get*SDIO_REG_RDWR_DATA) & 0xFF);
}

/* This function writes SDIO Register value */
static inline __inline void sdio_write_sd_reg(u32 addr, u8 data)
{
    u32 temp = 0;

    while(get(SDIO_REG_RDWR_DATA) & SD_VALID_BIT)
        temp += 1;
    temp = ((((addr & 0x00FFFFFF) << 8) & 0x0FFFFF00)) |
           (SD_VALID_BIT | SD_RD_WR_BIT);
    temp |= (data & 0x000000FF);
    set(SDIO_REG_RDWR_DATA, temp);
}

static inline __inline void write_sd_reg_sync(int func, int addr, u8 data)
{
	u32 temp = 0;

	while(get(SDIO_REG_RDWR_DATA) & SD_VALID_BIT)
		temp += 1;
	temp = ( 0x30000000 )|( (func&0x07)<<25 )|( addr<<8 )|( data );
	set(SDIO_REG_RDWR_DATA, temp);
}

/* This function writes SDIO Register value */
static inline __inline void wait_for_sdio_io_en(void)
{
    u32 temp = 0;
    u8 temp1 = 0;

    do {
        temp1 = sdio_read_sd_reg((u32)(rSDIO_IO_ENABLE));
        temp += 1;
    } while(!(temp1 & BIT(1)));
}

/* This function writes SDIO Register value */
static inline __inline void wait_for_sdio_start_card_init(void)
{
    while(!(getb(SDIO_STATE_CONTROL_REG) & START_CARD_INIT_EN)) ;
}

/* This function writes SDIO Register value */
static inline __inline void set_sdio_card_ready_flag(void)
{
    set(SDIO_STATE_CONTROL_REG, CARD_READY_FLAG);
}

/*
 * Host will catch an interrupt.
 */
static inline __inline void set_sdio_rx_compl_flag(void)
{
	u8 reg;

	reg = getb(SDIO_STATE_CONTROL_REG);
	reg |= 0x10;

	setb(SDIO_STATE_CONTROL_REG, reg);
}

#endif /* __SDIO_H_ */

